package saati.core.clustering;

import org.apache.commons.math.stat.clustering.Cluster;
import org.apache.commons.math.stat.clustering.EuclideanIntegerPoint;
import org.apache.commons.math.stat.clustering.KMeansPlusPlusClusterer;
import saati.utils.ArrayUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class KMeansClusterer {

    private int[][] inputData;
    private int countOfClusters;
    private KMeansPlusPlusClusterer<EuclideanIntegerPoint> clusterer;

    public KMeansClusterer(int[][] inputData, int countOfClusters) {
        this.inputData = inputData;
        this.countOfClusters = countOfClusters;
        this.clusterer = new KMeansPlusPlusClusterer<EuclideanIntegerPoint>(new Random());
    }

    public List<Integer[][]> cluster(int f1, int f2) {
        List<EuclideanIntegerPoint> pointList = getPointList(inputData);
        List<Cluster<EuclideanIntegerPoint>> clusters = clusterer.cluster(pointList, countOfClusters, 10);
        while ((clusters.get(0).getPoints().size() != f1) && (clusters.get(1).getPoints().size() != f2)) {
            clusters = clusterer.cluster(pointList, countOfClusters, 10);
        }
        return composeResult(clusters);
    }

    private List<Integer[][]> composeResult(List<Cluster<EuclideanIntegerPoint>> clusters) {
        List<Integer[][]> composedResult = new ArrayList<Integer[][]>();
        for (Cluster<EuclideanIntegerPoint> c : clusters) {
            Integer[][] res = composeResultMatrix(c);
            composedResult.add(res);
        }
        return composedResult;

    }

    private Integer[][] composeResultMatrix(Cluster<EuclideanIntegerPoint> c) {
        int expCount = c.getPoints().size();
        int altCount = c.getPoints().get(0).getPoint().length;
        Integer[][] res = new Integer[altCount][expCount];
        for (int i = 0; i < expCount; i++) {
            for (int j = 0; j < altCount; j++) {
                res[j][i] = c.getPoints().get(i).getPoint()[j];
            }
        }
        return res;
    }

    private List<EuclideanIntegerPoint> getPointList(int[][] inputData) {
        List<EuclideanIntegerPoint> pointList = new ArrayList<EuclideanIntegerPoint>();
        for (int expIndex = 0; expIndex < inputData[0].length; expIndex++) {
            pointList.add(new EuclideanIntegerPoint(ArrayUtil.getColumn(inputData, expIndex)));
        }
        return pointList;
    }

}

